package com.ftwj.demo.utils.validate;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import com.ftwj.demo.utils.StringHelper;
/**
*
* <p>Title: RegexpUtil</p>  
* <p>Description: 描述: ValidateUtil的补充类</p>
* <p>Copyright: Copyright (c) 2020 </p>  
* <p>Company: 兰州飞天网景信息产业有限公司</p>    
* @author WangSLi
* @date 2020年6月1日 下午5:53:12
* @version V1.0
 */
public class RegexpUtil {
	
	/**
	 * 匹配日期
	 * 格式(首位不为0): XXXX-XX-XX或 XXXX-X-X
	 */
	public static final String DATE_BARS_REGEXP = "^((((19){1}|(20){1})\\d{2})|\\d{2})-[0,1]?\\d{1}-[0-3]?\\d{1}$";
	
	/**
	 * 匹配日期
	 * 格式: XXXX/XX/XX
	 */
	public static final String DATE_SLASH_REGEXP = "^[0-9]{4}/(((0[13578]|(10|12))/(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)/(0[1-9]|[1-2][0-9]|30)))$";

	/**
	 * 匹配电话
	 * 
	 * 格式为: 0XXX-XXXXXX(10-13位首位必须为0) 或0XXX XXXXXXX(10-13位首位必须为0) 或
	 * (0XXX)XXXXXXXX(11-14位首位必须为0) 或 XXXXXXXX(6-8位首位不为0) 或 XXXXXXXXXXX(11位首位不为0)
	 * 匹配 : 0371-123456 或 (0371)1234567 或 (0371)12345678 或 010-123456 或 010-12345678
	 */
	public static final String PHONE_REGEXP = "^(?:0[0-9]{2,3}[-//s]{1}|//(0[0-9]{2,4}//))[0-9]{6,8}$|^[1-9]{1}[0-9]{5,7}$|^[1-9]{1}[0-9]{10}$";
	
	/**
	 * 匹配移动电话正则表达式
	 */
	public static final String MOBLIE_REGEXP = "^(13[0-9]|14[5-9]|15[0-3,5-9]|16[2,5,6,7]|17[0-8]|18[0-9]|19[1,3,5,8,9])\\\\d{8}$";
	
	 /**
     * 中国移动：China Mobile
     *13[4-9],147,148,15[0-2,7-9],165,170[3,5,6],172,178,18[2-4,7-8],19[5,8]
     */
	public static final String CM_NUM_MOBLIE_REGEXP = "^((13[4-9])|(14[7-8])|(15[0-2,7-9])|(165)|(178)|(18[2-4,7-8])|(19[5,8]))\\d{8}|(170[3,5,6])\\d{7}$";
    
    /**
     * 中国联通：China Unicom
     * 130,131,132,145,146,155,156,166,167,170[4,7,8,9],171,175,176,185,186
     */
	public static final String CU_NUM_MOBLIE_REGEXP = "^((13[0-2])|(14[5,6])|(15[5-6])|(16[6-7])|(17[1,5,6])|(18[5,6]))\\d{8}|(170[4,7-9])\\d{7}$";
    
    /**
     * 中国电信：China Telecom
     * 133,149,153,162,170[0,1,2],173,174[0-5],177,180,181,189,19[1,3,9]
     */
	public static final String CT_NUM_MOBLIE_REGEXP = "^((133)|(149)|(153)|(162)|(17[3,7])|(18[0,1,9])|(19[1,3,9]))\\d{8}|((170[0-2])|(174[0-5]))\\d{7}$";
	/**
	 * 
	 * 匹配cron表达式正则
	 * 这里真的不建议用正则匹配的方法验证cron表达式，一是匹配规则太复杂几乎没法修改，二是容易出错(由评论区@HLMK 指出)。比如上述的正则中0 0 0 L * ?就不能匹配到，所以强烈建议你使用CronExpression包进行验证。
	 *
	 *	CronExpression验证
	 *	这个方法需要引入quartz的jar包，然后只需要一行代码就可以验证了。
	 *	CronExpression.isValidExpression(cron)
	 */
	public static final String CRON_REGEXP = "^\\s*($|#|\\w+\\s*=|(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?(?:,(?:[0-5]?\\d)(?:(?:-|\\/|\\,)(?:[0-5]?\\d))?)*)\\s+(\\?|\\*|(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?(?:,(?:[01]?\\d|2[0-3])(?:(?:-|\\/|\\,)(?:[01]?\\d|2[0-3]))?)*)\\s+(\\?|\\*|(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?(?:,(?:0?[1-9]|[12]\\d|3[01])(?:(?:-|\\/|\\,)(?:0?[1-9]|[12]\\d|3[01]))?)*)\\s+(\\?|\\*|(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?(?:,(?:[1-9]|1[012])(?:(?:-|\\/|\\,)(?:[1-9]|1[012]))?(?:L|W)?)*|\\?|\\*|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?(?:,(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)*)\\s+(\\?|\\*|(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?(?:,(?:[0-6])(?:(?:-|\\/|\\,|#)(?:[0-6]))?(?:L)?)*|\\?|\\*|(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?(?:,(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?)*)(|\\s)+(\\?|\\*|(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?(?:,(?:|\\d{4})(?:(?:-|\\/|\\,)(?:|\\d{4}))?)*))$";
	
	/**
	 * 匹配身份证号码
	 * 1 地址码规则： 地址码长6位 以数字1-9开头 后5位为0-9的数字 根据以上规则，写出地址码的正则表达式： /^[1-9]\d{5}/
	 * 2 年份码规则： 年份码长4位 以数字18，19或20,30开头 剩余两位为0-9的数字 根据以上规则，写出年份码的正则表达式： /(18|19|20|(3\d))\d{2}/。如果想扩展可以自己添加
	 * 3 月份码规则： 月份码长2位 第一位数字为0，第二位数字为1-9 或者第一位数字为1，第二位数字为0-2 根据以上规则，写出月份码的正则表达式： /((0[1-9])|(1[0-2]))/。
	 * 4 日期码规则： 日期码长2位 第一位数字为0-2，第二位数字为1-9 或者是10，20，30，31 根据以上规则，写出日期码的正则表达式 ：/(([0-2][1-9])|10|20|30|31)/。
	 * 5 顺序码规则： 顺序码长3位 顺序码是数字 根据以上规则，写出顺序码的正则表达式 ：/\d{3}/。
	 * 6 校验码规则： 校验码长1位 可以是数字，字母x或字母X 根据以上规则，写出校验码的正则表达式 ：/[0-9Xx]/。
	 */
	public static final String  ID_CARD_REGEXP="/^[1-9]\\d{5}(18|19|20|(3\\d))\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$/";

	/**
	 * 匹配邮编代码
	 * 格式为: XXXXXX(6位)
	 */
	public static final String ZIP_REGEXP = "^[0-9]{6}$";// 匹配邮编代码
	
	/**
	 * 匹配非负整数（正整数 + 0)
	 */
	public static final String NON_NEGATIVE_INTEGERS_REGEXP = "^//d+$";

	/**
	 * 匹配不包括零的非负整数（正整数 > 0)
	 */
	public static final String NON_ZERO_NEGATIVE_INTEGERS_REGEXP = "^[1-9]+//d*$";

	/**
	 * 匹配正整数
	 */
	public static final String POSITIVE_INTEGER_REGEXP = "^[0-9]*[1-9][0-9]*$";

	/**
	 * 匹配非正整数（负整数 + 0）
	 */
	public static final String NON_POSITIVE_INTEGERS_REGEXP = "^((-//d+)|(0+))$";

	/**
	 * 匹配负整数
	 */
	public static final String NEGATIVE_INTEGERS_REGEXP = "^-[0-9]*[1-9][0-9]*$";

	/**
	 * 匹配整数
	 */
	public static final String INTEGER_REGEXP = "^-?//d+$";

	/**
	 * 匹配非负浮点数（正浮点数 + 0）
	 */
	public static final String NON_NEGATIVE_RATIONAL_NUMBERS_REGEXP = "^//d+(//.//d+)?$";

	/**
	 * 匹配正浮点数
	 */
	public static final String POSITIVE_RATIONAL_NUMBERS_REGEXP = "^(([0-9]+//.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*//.[0-9]+)|([0-9]*[1-9][0-9]*))$";

	/**
	 * 匹配非正浮点数（负浮点数 + 0）
	 */
	public static final String NON_POSITIVE_RATIONAL_NUMBERS_REGEXP = "^((-//d+(//.//d+)?)|(0+(//.0+)?))$";

	/**
	 * 匹配负浮点数
	 */
	public static final String NEGATIVE_RATIONAL_NUMBERS_REGEXP = "^(-(([0-9]+//.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*//.[0-9]+)|([0-9]*[1-9][0-9]*)))$";

	/**
	 * 匹配浮点数
	 */
	public static final String RATIONAL_NUMBERS_REGEXP = "^(-?//d+)(//.//d+)?$";

	/**
	 * 匹配由26个英文字母组成的字符串
	 */
	public static final String LETTER_REGEXP = "^[A-Za-z]+$";

	/**
	 * 匹配由26个英文字母的大写组成的字符串
	 */
	public static final String UPWARD_LETTER_REGEXP = "^[A-Z]+$";

	/**
	 * 匹配由26个英文字母的小写组成的字符串
	 */
	public static final String LOWER_LETTER_REGEXP = "^[a-z]+$";

	/**
	 * 匹配由数字和26个英文字母组成的字符串
	 */
	public static final String LETTER_NUMBER_REGEXP = "^[A-Za-z0-9]+$";

	/**
	 * 匹配由数字、26个英文字母或者下划线组成的字符串
	 */
	public static final String LETTER_NUMBER_UNDERLINE_REGEXP = "^//w+$";

	/**
	 * 通过身份证获取生日
	 *
	 * @param idNumber 身份证号
	 * @return 返回生日, 格式为 yyyy-MM-dd 的字符串
	 */
	public static String getBirthdayByIdNumber(String idNumber) {

		String birthday = "";
		if (idNumber.length() == 15) {
			birthday = "19" + idNumber.substring(6, 8) + "-" + idNumber.substring(8, 10) + "-"
					+ idNumber.substring(10, 12);
		} else if (idNumber.length() == 18) {
			birthday = idNumber.substring(6, 10) + "-" + idNumber.substring(10, 12) + "-" + idNumber.substring(12, 14);
		}
		return birthday;

	}

	/**
	 * 通过身份证获取性别
	 *
	 * @param idNumber 身份证号
	 * @return 返回性别, 0 保密 , 1 男 2 女
	 */
	public static Integer getGenderByIdNumber(String idNumber) {

		int gender = 0;
		if (idNumber.length() == 15) {
			gender = Integer.parseInt(String.valueOf(idNumber.charAt(14))) % 2 == 0 ? 2 : 1;
		} else if (idNumber.length() == 18) {
			gender = Integer.parseInt(String.valueOf(idNumber.charAt(16))) % 2 == 0 ? 2 : 1;
		}
		return gender;

	}

	/**
	 * 通过身份证获取年龄
	 *
	 * @param idNumber     身份证号
	 * @param isNominalAge 是否按元旦算年龄，过了1月1日加一岁 true : 是 false : 否
	 * @return 返回年龄
	 */
	public static Integer getAgeByIdNumber(String idNumber, boolean isNominalAge) {

		String birthString = getBirthdayByIdNumber(idNumber);
		if (StringHelper.isEmpty(birthString)) {
			return 0;
		}
		return getAgeByBirthString(birthString, isNominalAge);

	}

	/**
	 * 通过生日日期获取年龄
	 *
	 * @param birthDate 生日日期
	 * @return 返回年龄
	 */
	public static Integer getAgeByBirthDate(Date birthDate) {
		return getAgeByBirthString(new SimpleDateFormat("yyyy-MM-dd").format(birthDate));

	}

	/**
	 * 通过生日字符串获取年龄
	 *
	 * @param birthString 生日字符串
	 * @return 返回年龄
	 */
	public static Integer getAgeByBirthString(String birthString) {
		return getAgeByBirthString(birthString, "yyyy-MM-dd");

	}

	/**
	 * 通过生日字符串获取年龄
	 *
	 * @param birthString  生日字符串
	 * @param isNominalAge 是否按元旦算年龄，过了1月1日加一岁 true : 是 false : 否
	 * @return 返回年龄
	 */
	public static Integer getAgeByBirthString(String birthString, boolean isNominalAge) {
		return getAgeByBirthString(birthString, "yyyy-MM-dd", isNominalAge);

	}

	/**
	 * 通过生日字符串获取年龄
	 *
	 * @param birthString 生日字符串
	 * @param format      日期字符串格式,为空则默认"yyyy-MM-dd"
	 * @return 返回年龄
	 */
	public static Integer getAgeByBirthString(String birthString, String format) {
		return getAgeByBirthString(birthString, "yyyy-MM-dd", false);
	}

	/**
	 * 通过生日字符串获取年龄
	 *
	 * @param birthString  生日字符串
	 * @param format       日期字符串格式,为空则默认"yyyy-MM-dd"
	 * @param isNominalAge 是否按元旦算年龄，过了1月1日加一岁 true : 是 false : 否
	 * @return 返回年龄
	 */
	public static Integer getAgeByBirthString(String birthString, String format, boolean isNominalAge) {
		int age = 0;
		if (StringHelper.isEmpty(birthString)) {
			return age;
		}
		if (StringHelper.isEmpty(format)) {
			format = "yyyy-MM-dd";
		}
		try {

			Calendar birthday = Calendar.getInstance();
			Calendar today = Calendar.getInstance();
			SimpleDateFormat sdf = new SimpleDateFormat(format);
			birthday.setTime(sdf.parse(birthString));
			age = today.get(Calendar.YEAR) - birthday.get(Calendar.YEAR);
			if (!isNominalAge) {
				if (today.get(Calendar.MONTH) < birthday.get(Calendar.MONTH)
						|| (today.get(Calendar.MONTH) == birthday.get(Calendar.MONTH)
								&& today.get(Calendar.DAY_OF_MONTH) < birthday.get(Calendar.DAY_OF_MONTH))) {
					age = age - 1;
				}
			}
		} catch (ParseException e) {
			e.printStackTrace();
		}

		return age;

	}
}
